﻿using MiniFox.Caching;
using MiniFox.Components;
using MiniFox.Infrastructure;
using MiniFox.Platform.Applications.Configurations;
using MiniFox.Platform.Applications.Models;
using MiniFox.Platform.Models;
using MiniFox.Utility;
using MiniFox.Web;
using Newtonsoft.Json;
using System.Security.Claims;
using System.Text.RegularExpressions;

namespace MiniFox.Platform.Applications.Auth
{
    /// <summary>
    /// 
    /// </summary>
    [AutoRegister, SpareComponent(typeof(UserAuthClient))]
    public class UserAuthClient : AuthorizationClient
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="stateManagement"></param>
        public UserAuthClient(IAuthStateManagement stateManagement) : base(stateManagement)
        {
            stateManagement.CookieStateExpires = this.CookieStateExpires;
        }
        /// <summary>
        /// 
        /// </summary>
        [AutoConfigure]
        protected UserAuthClientConfiguration Configuration
        {
            get;
            set;
        }

        #region miniAuth config values
        /// <summary>
        /// 
        /// </summary>
        [JsonIgnore]
        public int CookieStateExpires
        {
            get { return this.Configuration.CookieStateExpires; }
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonIgnore]
        public virtual string ApplicationID
        {
            get { return this.Configuration.ApplicationID; }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri CreateTokenUrl
        {
            get { return new Uri(this.Configuration.FullIssueTokenUrl); }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri RenewTokenUrl
        {
            get { return new Uri(this.Configuration.FullRenewTokenUrl); }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri AuthenticationUrl
        {
            get { return new Uri(this.Configuration.FullAuthUrl); }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri AgentConnectUrl
        {
            get { return new Uri(WebUtil.BuildFullUrl(this.StateManagement.FullHost, this.Configuration.AgentConnectUrl)); }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri IdentityUrl
        {
            get { return new Uri(this.Configuration.FullIdentityUrl); }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri ModuleRolesUrl
        {
            get { return new Uri(this.Configuration.FullModuleRolesUrl); }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri SignOutUrl
        {
            get { return new Uri(this.Configuration.FullSignOutUrl); }
        }
        /// <summary>
        /// 
        /// </summary>
        public Uri DefaultUrl
        {
            get { return new Uri(this.Configuration.DefaultUrl); }
        }
        /// <summary>
        /// 
        /// </summary>
        protected virtual CacheProvider Cache
        {
            get
            {
                return CacheService.DefaultCachingProvider;
            }
        }
        #endregion

        #region auth logic
        /// <summary>
        /// 
        /// </summary>
        /// <param name="redirectUri"></param>
        /// <returns></returns>
        public Uri GetAuthUrl(Uri redirectUri)
        {
            return GetAuthUrl(redirectUri, SequenceGUID.NewGUID());
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="redirectUri"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public Uri GetAuthUrl(Uri redirectUri, string context)
        {
            return GetAuthUrl(redirectUri, context, AuthResponseType.AuthCode, AuthScope.Standard);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="redirectUri"></param>
        /// <param name="context"></param>
        /// <param name="authResponseType"></param>
        /// <param name="authScope"></param>
        /// <returns></returns>
        public virtual Uri GetAuthUrl(Uri redirectUri, string context, AuthResponseType authResponseType, AuthScope authScope)
        {
            if (redirectUri == null)
            {
                redirectUri = this.DefaultUrl;
            }
            Cache.Add(context, redirectUri);
            IDictionary<string, object> parameters = new Dictionary<string, object>
            {
                { OpenAuthKey.AUTH_REDIRECT_URI, this.AgentConnectUrl },
                { OpenAuthKey.AUTH_APP_ID, ApplicationID },
                { OpenAuthKey.AUTH_RESPONSE_TYPE, authResponseType },
                { OpenAuthKey.AUTH_SCOPE, authScope },
                { OpenAuthKey.AUTH_CLIENT_CONTEXT, context }
            };

            return this.AuthenticationUrl.AddQueryString(parameters);
        }
        /// <summary>
        /// 回接授权码，验证通过后提取重定向地址
        /// </summary>
        /// <param name="authCode"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public Uri GetRedirectUrl(string authCode, string context)
        {
            if (CreateAuthorizeToken(authCode))
            {
                return Cache.Get<Uri>(context);
            }
            else
            {
                return null;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public virtual string RefreshToken
        {
            get
            {
                string refreshToken = this.StateManagement.Token;
                return refreshToken;
            }
            set
            {
                string refreshToken = value;
                this.StateManagement.Token = refreshToken;
            }
        }
        /// <summary>
        /// 验证当前令牌状态
        /// </summary>
        /// <returns></returns>
        public override bool VerifyTokenState()
        {
            var token = this.AuthorizeToken;
            bool validState = ((token != null)) && this.Identity != null;
            if (!validState)//校验失败时，清除状态
            {
                ClearState();
            }

            return validState;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="authToken"></param>
        protected virtual void SetTokenState(AuthorizeToken authToken)
        {
            this._authorizeToken = authToken;
            string refreshToken = authToken.RefreshToken;
            //重置 SECURE_CLIENT_TOKEN 状态（cookie及http头）
            this.RefreshToken = refreshToken;
            //以refresh token 为key 缓存 授权对象
            Cache.Add(refreshToken, authToken);
        }
        /// <summary>
        /// 
        /// </summary>
        public override void ClearState()
        {

            if (!string.IsNullOrEmpty(this.RefreshToken))
            {
                Cache.Remove(this.RefreshToken);
            }
            this.StateManagement.Clear();
        }
        
        AuthorizeToken _authorizeToken;
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected virtual AuthorizeToken AuthorizeToken
        {
            get
            {
                if (_authorizeToken != null)
                    return _authorizeToken;

                string refreshToken = this.RefreshToken;

                if (string.IsNullOrEmpty(refreshToken))
                {
                    return null;
                }

                _authorizeToken = Cache.Get<AuthorizeToken>(refreshToken);
                if (_authorizeToken == null)
                {
                    //刷新access token
                    ResetAuthorizeToken(refreshToken, AuthGrantType.GetToken); 
                }
                else if (_authorizeToken.AccessTokenExpired())
                {
                    //刷新access token
                    ResetAuthorizeToken(refreshToken, AuthGrantType.RefreshToken);
                }
                    
                return _authorizeToken;
            }
        }

        private AuthorizeToken ResetAuthorizeToken(string refreshToken, AuthGrantType authGrantType)
        {
            try
            {
                Cache.Remove(refreshToken);
                _authorizeToken = RenewAuthorizeToken(refreshToken, authGrantType);
                if (_authorizeToken != null)
                    Cache.Add(refreshToken, _authorizeToken, 600);

                return _authorizeToken;
            }
            catch (Exception e)
            {
                this.StateManagement.Clear();
                throw e;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="refreshToken"></param>
        /// <param name="authGrantType"></param>
        /// <returns></returns>
        protected virtual AuthorizeToken RenewAuthorizeToken(string refreshToken, AuthGrantType authGrantType)
        {
            var uri = RenewTokenUrl;
            uri = uri.AddQueryString(OpenAuthKey.AUTH_APP_ID, ApplicationID);
            uri = uri.AddQueryString(OpenAuthKey.AUTH_SECRET_KEY, this.Configuration.SecretKey);
            uri = uri.AddQueryString(OpenAuthKey.AUTH_REFRESH_TOKEN, refreshToken);
            uri = uri.AddQueryString(OpenAuthKey.AUTH_GRANT_TYPE, authGrantType.ToString());
            try
            {
                var res = uri.HttpGet<Response<AuthorizeToken>>();
                if (res.Success)
                {
                    var authToken = res.Data;
                    return authToken;
                }
                else
                {
                    this.WriteInfoLog(res.ToJsonString());
                    return null;
                }
            }
            catch (Exception e)
            {
                this.WriteFatalLog(e);
                throw new MiniFox.Exceptions.SystemException(e);
            }
        }
        /// <summary>
        /// 授权码换sso token
        /// </summary>
        /// <param name="authCode"></param>
        /// <returns></returns>
        public virtual bool CreateAuthorizeToken(string authCode)
        {
            if (string.IsNullOrEmpty(authCode))
                return false;
            //获取access token
            var uri = CreateTokenUrl;
            uri = uri.AddQueryString(OpenAuthKey.AUTH_APP_ID, ApplicationID);
            uri = uri.AddQueryString(OpenAuthKey.AUTH_SECRET_KEY, this.Configuration.SecretKey);
            uri = uri.AddQueryString(OpenAuthKey.AUTH_CODE, authCode);
            uri = uri.AddQueryString(OpenAuthKey.AUTH_GRANT_TYPE, AuthGrantType.AuthorizationCode.ToString());
            try
            {
                var res = uri.HttpGet<Response<AuthorizeToken>>();
                if (res.Success)
                {
                    this.SetTokenState(res.Data);
                }
                else
                {
                    this.WriteInfoLog(res.ToJsonString());
                }
                return res.Success;
            }
            catch (Exception e)
            {
                this.WriteFatalLog(e);
                throw new MiniFox.Exceptions.SystemException(e);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override SystemIdentity GetIdentity()
        {
            if (this.AuthorizeToken == null) return null;
            var token = this.AuthorizeToken.AccessToken;
            var user = GetIdentity(token);

            if (user == null)
            {
                this.ResetAuthorizeToken(this.RefreshToken, AuthGrantType.GetToken);
                user = GetIdentity(token);
            }
            return user;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        protected internal override SystemIdentity GetIdentity(string accessToken)
        {
            var uri = IdentityUrl;
            uri = uri.AddQueryString(OpenAuthKey.AUTH_ACCESS_TOKEN, accessToken);
            try
            {
                var res = uri.HttpGet<Response<OpenAuthIdentity>>();
                if (res.Success)
                {
                    OpenAuthIdentity identity = res.Data;
                    return identity;
                }
                else
                {
                    this.WriteInfoLog(res.ToJsonString());
                    return null;
                }

            }
            catch (Exception e)
            {
                this.WriteFatalLog(e);
                throw new MiniFox.Exceptions.SystemException(e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public override bool IsExceptionUrl(string url)
        {
            return this.Configuration.ExceptionUrls != null && this.Configuration.ExceptionUrls.Any(str => string.Equals(url, str, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(url, str, RegexOptions.IgnoreCase));
        }
        #endregion

        #region roles

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fullUrl"></param>
        /// <returns></returns>
        public override string[] GetRoles(string fullUrl)
        {
            return GetRoles(fullUrl, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="requestUrl"></param>
        /// <param name="entryUrl"></param>
        /// <returns></returns>
        public override string[] GetRoles(string requestUrl, string entryUrl)
        {
            Uri uri = this.ModuleRolesUrl;
            try
            {
                var res = uri.HttpPostJson<RolesQuery, Response<string[]>>(new RolesQuery
                {
                     FullUri = requestUrl, EntryUri = entryUrl
                });
                if (res.Success)
                {
                    return res.Data;
                }
                else
                {
                    this.WriteInfoLog(res.ToJsonString());
                    return null;
                }

            }
            catch (Exception e)
            {
                this.WriteFatalLog(e);
                throw new MiniFox.Exceptions.SystemException(e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override ClaimsPrincipal CreatePrincipal()
        {
            //获取当前用户所有角色
            var roles = this.Identity.Roles;
            var principal = new SystemPrincipal(this.Identity, roles, this.AuthorizeToken, this.Configuration.SysAdmin);
            return principal;
        }

        #endregion
    }
}
